#include "../stdafx.h"
#include "TileManager.h"

#include "../source/Agent.h"
#include "../source/Player.h"
#include "../source/Ball.h"
#include "Layer.h"
#include "Tile.h"
#include "../TinyXML/tinyxml.h"
#include "../ScoreMessage.h"
#include "../Game.h"


CTileManager* CTileManager::s_pInstance;

CTileManager::~CTileManager()
{
	delete m_pMap;
}


void CTileManager::Render()
{
	if (m_pMap != nullptr)
		m_pMap->Render();
}

void CTileManager::Update(float dt)
{
	if (m_pMap != nullptr)
		m_pMap->Update(dt);
}

CTileManager* CTileManager::GetInstance()
{
	if (s_pInstance == nullptr)
		s_pInstance = new CTileManager;
	return s_pInstance;
}

void CTileManager::DeleteInstance()
{
	delete CTileManager::s_pInstance;
	CTileManager::s_pInstance = nullptr;
}

//Handles Tile Collision
//Will see which tiles the feet collide with and applies affects to the collider
void CTileManager::AgentCollision(const SGD::Rectangle &footRect, CAgent* collider)
{
	bool slow = false, damage = false, score = false, custom = false;
	string customType;
	for (size_t i = 0; i < m_pMap->m_vTiles.size(); i++)
	{
		//If feet collide with a rect
		if (footRect.IsIntersecting(m_pMap->m_vTiles[i]->GetRect()) == true)
		{
			//Collides with slowing tile
			if (m_pMap->m_vTileTypes[m_pMap->m_vTiles[i]->GetType()] == "SLOW_HAZARD")
				slow = true;
			//Collides with damaging tile
			else if (m_pMap->m_vTileTypes[m_pMap->m_vTiles[i]->GetType()] == "DAMAGE_HAZARD")
				damage = true;
			//Collides with red goal zone
			else if (m_pMap->m_vTileTypes[m_pMap->m_vTiles[i]->GetType()] == "RED_GOAL" && dynamic_cast<CPlayer*>(collider) != nullptr
				&& dynamic_cast<CPlayer*>(collider)->GetTeam() == true && dynamic_cast<CPlayer*>(collider)->GetCarrying() == true)
			{
				ScoreMessage* pScore = new ScoreMessage(m_pMap->m_vTiles[i]->GetType());
				// Queue it
				pScore->QueueMessage();
				pScore = nullptr;
			}
			//Collides with blue goal zone
			else if (m_pMap->m_vTileTypes[m_pMap->m_vTiles[i]->GetType()] == "BLUE_GOAL" && dynamic_cast<CPlayer*>(collider) != nullptr
				&& dynamic_cast<CPlayer*>(collider)->GetTeam() == false && dynamic_cast<CPlayer*>(collider)->GetCarrying() == true)
			{
				ScoreMessage* pScore = new ScoreMessage(m_pMap->m_vTiles[i]->GetType());
				// Queue it
				pScore->QueueMessage();
				pScore = nullptr;
			}
			//Collides with custom event tile
			else if (m_pMap->m_vTileTypes[m_pMap->m_vTiles[i]->GetType()] != "BASE_TILE")
			{
				custom = true;
				customType = m_pMap->m_vTileTypes[m_pMap->m_vTiles[i]->GetType()];
			}

		}
	}


	//Apply effects
	if (slow == true)
	{
		SGD::Event* slowEvent = new SGD::Event("HAZARD_SLOW", nullptr, this);

		slowEvent->QueueEvent(collider);
	}
	if (damage == true)
	{
		SGD::Event* damEvent = new SGD::Event("HAZARD_DAMAGE", nullptr, this);

		damEvent->QueueEvent(collider);
	}
	if (custom == true)
	{
		SGD::Event* customEvent = new SGD::Event(customType.c_str(), nullptr, this);

		customEvent->QueueEvent();
	}
}

//Will check if ball enters goal
bool CTileManager::BallCollision(const SGD::Rectangle &footRect, CBall* collider)
{
	for (size_t i = 0; i < m_pMap->m_vTiles.size(); i++)
	{
		//If feet collide with a rect
		if (footRect.IsIntersecting(m_pMap->m_vTiles[i]->GetRect()) == true)
		{
			//Collides with red goal zone
			if ((m_pMap->m_vTileTypes[m_pMap->m_vTiles[i]->GetType()] == "RED_GOAL" || m_pMap->m_vTileTypes[m_pMap->m_vTiles[i]->GetType()] == "BLUE_GOAL") && collider->IsHeld() == false)
			{
				collider->SetVelocity({ 0, 0 });
				collider->SetAerial(false);
				// Create a score message
				ScoreMessage* pScore = new ScoreMessage(m_pMap->m_vTiles[i]->GetType());

				// Queue it
				pScore->QueueMessage();
				pScore = nullptr;
				return true;
			}
		}
	}
	return false;
}


//Loads a map from an xml file
void CTileManager::LoadLevel(char* filePath)
{
	//Makes sure there isn't already a map loaded
	if (m_pMap != nullptr)
		return;
	string tempString = "Resources/Maps/";
	tempString += filePath;
	//Open xml file
	TiXmlDocument file;
	if (file.LoadFile(tempString.c_str()))
	{
		//Identify root element (Map)
		TiXmlElement* root = file.RootElement();
		if (root)
		{
			//Temp values - double needed for tinyXML
			double dTemp;
			int nTemp;
			TiXmlElement* child = nullptr;
			TiXmlElement* grandchild = nullptr;

			//Create element pointer
			//Skips Backgrounds for now
			TiXmlElement* element = root->FirstChildElement("Tiles");
			child = element->FirstChildElement("TileSet");
			//Load World Map using tileset location
			tempString = "Resources/Maps/";
			tempString += child->GetText();
			m_pMap = new CWorldMap(tempString.c_str());

			//Get tile and map details (again out of order)
			element->Attribute("tileW", &nTemp);
			m_pMap->m_szTileSize.width = (float)nTemp;
			element->Attribute("tileH", &nTemp);
			m_pMap->m_szTileSize.height = (float)nTemp;
			element->Attribute("mapW", &nTemp);
			m_pMap->m_szMapSize.width = (float)nTemp;
			element->Attribute("mapH", &nTemp);
			m_pMap->m_szMapSize.height = (float)nTemp;

			//Load in tile types
			child = element->FirstChildElement("Events");
			child->Attribute("count", &nTemp);
			for (int i = 0; i < nTemp; i++)
			{
				if (i == 0)
					grandchild = child->FirstChildElement("Event");
				else
					grandchild = grandchild->NextSiblingElement("Event");

				tempString = grandchild->Attribute("type");
				//Store this tile type
				m_pMap->m_vTileTypes.push_back(tempString);
			}
			//Find Top Left Corner
			SGD::Point topLeft;
			topLeft.x = (Game::GetInstance()->GetScreenWidth() - (m_pMap->m_szMapSize.width * m_pMap->m_szTileSize.width)) / 2;
			topLeft.y = (Game::GetInstance()->GetScreenHeight() - (m_pMap->m_szMapSize.height * m_pMap->m_szTileSize.height)) / 2;

			//Load in tiles
			SGD::Rectangle tempRect;
			SGD::Point tempPoint;

			child = element->FirstChildElement("Row");
			for (int h = 0; h < (int)m_pMap->m_szMapSize.height; h++)
			{
				grandchild = child->FirstChildElement("Tile");
				for (int w = 0; w < (int)m_pMap->m_szMapSize.width; w++)
				{
					tempRect.top = topLeft.y + m_pMap->m_szTileSize.height * h;
					tempRect.left = topLeft.x + m_pMap->m_szTileSize.width * w;
					tempRect.bottom = tempRect.top + m_pMap->m_szTileSize.height;
					tempRect.right = tempRect.left + m_pMap->m_szTileSize.width;
					grandchild->Attribute("source", &nTemp);
					tempPoint.x = (float)(nTemp % (int)m_pMap->m_szMapSize.width) * m_pMap->m_szTileSize.width;
					tempPoint.y = (float)(nTemp / (int)m_pMap->m_szMapSize.height) * m_pMap->m_szTileSize.height;
					grandchild->Attribute("event", &nTemp);
					CTile* tempTile = new CTile(tempRect, tempPoint, nTemp);
					m_pMap->m_vTiles.push_back(tempTile);

					grandchild = grandchild->NextSiblingElement("Tile");
				}
				child = child->NextSiblingElement("Row");
			}

			//Load Backgrounds
			SGD::Size tempSize;
			element = root->FirstChildElement("Backgrounds");
			element->Attribute("layers", &nTemp);
			//For each background layer
			for (int i = 0; i < nTemp; i++)
			{
				if (i == 0)
					child = element->FirstChildElement("Layer");
				else
					child = child->NextSiblingElement("Layer");

				grandchild = child->FirstChildElement("Source");

				tempString = "Resources/Maps/";
				tempString += grandchild->GetText();

				grandchild = grandchild->NextSiblingElement("Parallax");
				int tempInt;
				grandchild->Attribute("intensity", &dTemp);
				grandchild->Attribute("x", &tempInt);
				tempPoint.x = (float)tempInt;
				grandchild->Attribute("y", &tempInt);
				tempPoint.y = (float)tempInt;

				grandchild->Attribute("w", &tempInt);
				tempSize.width = (float)tempInt;
				grandchild->Attribute("h", &tempInt);
				tempSize.height = (float)tempInt;

				grandchild->Attribute("wrap", &tempInt);
				bool tempBool = tempInt == 1;

				CLayer* layer = new CLayer(tempString.c_str(), tempSize, tempPoint, (float)dTemp, tempBool);
				m_pMap->m_vLayers.push_back(layer);
			}

			element = root->FirstChildElement("Entities");
			int tempInt;

			child = element->FirstChildElement("Ball");
			child->Attribute("x", &tempInt);
			tempPoint.x = float(tempInt * 32);
			child->Attribute("y", &tempInt);
			tempPoint.y = float(tempInt * 32);
			m_EntitySpawns.push_back(tempPoint);

			child = element->FirstChildElement("RedPlayers");
			int tempCount;
			child->Attribute("count", &tempCount);

			grandchild = child->FirstChildElement("Instance");
			for (int count = 0; count < tempCount; count++)
			{
				grandchild->Attribute("x", &tempInt);
				tempPoint.x = float(tempInt * 32);
				grandchild->Attribute("y", &tempInt);
				tempPoint.y = float(tempInt * 32);
				m_EntitySpawns.push_back(tempPoint);
				grandchild = grandchild->NextSiblingElement("Instance");
			}

			child = element->FirstChildElement("BluePlayers");
			child->Attribute("count", &tempCount);

			grandchild = child->FirstChildElement("Instance");
			for (int count = 0; count < tempCount; count++)
			{
				grandchild->Attribute("x", &tempInt);
				tempPoint.x = float(tempInt * 32);
				grandchild->Attribute("y", &tempInt);
				tempPoint.y = float(tempInt * 32);
				m_EntitySpawns.push_back(tempPoint);
				grandchild = grandchild->NextSiblingElement("Instance");
			}

			child = element->FirstChildElement("Boss");
			child->Attribute("count", &tempInt);

			if (tempInt > 0)
			{
				grandchild = child->FirstChildElement("Instance");
				grandchild->Attribute("x", &tempInt);
				tempPoint.x = float(tempInt * 32);
				grandchild->Attribute("y", &tempInt);
				tempPoint.y = float(tempInt * 32);
				m_EntitySpawns.push_back(tempPoint);
			}

			child = element->FirstChildElement("WallsTall");
			child->Attribute("count", &tempCount);

			grandchild = child->FirstChildElement("Instance");
			for (int count = 0; count < tempCount; count++)
			{
				grandchild->Attribute("x", &tempInt);
				tempPoint.x = float(tempInt * 32);
				grandchild->Attribute("y", &tempInt);
				tempPoint.y = float(tempInt * 32);
				m_EntitySpawns.push_back(tempPoint);
				grandchild = grandchild->NextSiblingElement("Instance");
			}
			m_nCount.push_back(tempCount);

			child = element->FirstChildElement("WallsShort");
			child->Attribute("count", &tempCount);

			grandchild = child->FirstChildElement("Instance");
			for (int count = 0; count < tempCount; count++)
			{
				grandchild->Attribute("x", &tempInt);
				tempPoint.x = float(tempInt * 32);
				grandchild->Attribute("y", &tempInt);
				tempPoint.y = float(tempInt * 32);
				m_EntitySpawns.push_back(tempPoint);
				grandchild = grandchild->NextSiblingElement("Instance");
			}
			m_nCount.push_back(tempCount);

			child = element->FirstChildElement("Enemies");
			child->Attribute("count", &tempCount);

			grandchild = child->FirstChildElement("Instance");
			for (int count = 0; count < tempCount; count++)
			{
				grandchild->Attribute("x", &tempInt);
				tempPoint.x = float(tempInt * 32);
				grandchild->Attribute("y", &tempInt);
				tempPoint.y = float(tempInt * 32);
				m_EntitySpawns.push_back(tempPoint);

				int numWaypoints = 0;
				grandchild->Attribute("NumWaypoints", &numWaypoints);

				if (numWaypoints > 0)
				{
					for (int waypoints = 0; waypoints < numWaypoints; waypoints++)
					{
						grandchild->Attribute("WaypointX", &tempInt);
						tempPoint.x = float(tempInt * 32);
						grandchild->Attribute("WaypointY", &tempInt);
						tempPoint.y = float(tempInt * 32);
						m_EnemyWaypoints.push_back(tempPoint);
					}
				}

				m_nNumWaypoints.push_back(numWaypoints);
				grandchild = grandchild->NextSiblingElement("Instance");
			}
			m_nCount.push_back(tempCount);
		}
	}

}

//Unloads a map
void CTileManager::UnloadLevel()
{
	delete m_pMap;
	m_pMap = nullptr;
}

//Saves the game
void CTileManager::SaveLevel(char* filePath)
{


}

SGD::Point CTileManager::SpawnEntity(int index)
{
	if (m_EntitySpawns.size() != 0)
		return m_EntitySpawns[index];
	else
		return SGD::Point(-1, -1);
}

// Get the waypoint for the specific enemy
SGD::Point CTileManager::GetWaypoints(int index)
{
	if (m_EnemyWaypoints.size() != 0)
		return m_EnemyWaypoints[index];
	else
		return SGD::Point(-1, -1);
}

// Get the number of waypoints for the specific enemy
int CTileManager::GetNumWaypoints(int index)
{
	return m_nNumWaypoints[index];
}